from dataclasses import dataclass
from typing import Callable

from graia.ariadne import Ariadne

from admin import error, test, global_
from admin.common import CommandSource
from app import instance


# 递归生成帮助列表
def generate_help_list(command_map: tuple, prefix: str = ""):
    message = []
    for command in command_map:
        command_a: CommandA = command
        if type(command_a.content) == tuple:
            message.append(f"{prefix}{command_a.command}:\n")
            message.append(generate_help_list(command_a.content, prefix + "  "))
        else:
            message.append(f"{prefix}{command_a.command}\n")
    return "".join(message)


async def on_help(app: Ariadne, source: CommandSource):
    await source.reply(app, root_help_text())


def root_help_text():
    return "【帮助】\n" + \
           "以顿号（、）开头为执行命令\n" + \
           "\n" + \
           "命令列表：\n" + \
           generate_help_list(root_command_map)


@dataclass
class CommandCall:
    callable: Callable
    default_arg: str | None = None


@dataclass
class CommandA:
    command: str
    help: str
    content: CommandCall | tuple

    def help_text(self, command_path: tuple):
        if type(self.content) == tuple:
            return f"【帮助】{''.join(command_path)}\n" + \
                   "\n" + \
                   f"{self.help}\n" + \
                   "需要在该命令后面跟子命令。\n" + \
                   "\n" + \
                   "子命令列表：\n" + \
                   generate_help_list(self.content)
        else:
            return f"【帮助】{''.join(command_path) + self.command}\n" + \
                   "\n" + \
                   f"{self.help}\n"


root_command_map = (
    CommandA("测试", "", CommandCall(test.on_master_test, "Hello World!")),
    CommandA("帮助", "", CommandCall(on_help)),
    CommandA("状态", "", CommandCall(global_.on_global_status)),
    CommandA("全局", "该命令是本帐号的全局配置", (
        CommandA("私聊", "是否启用私聊，若禁用，除机器人的主人外任何人私聊将不会得到回应", (
            CommandA("启用", "", CommandCall(global_.on_global_friend_enable)),
            CommandA("禁用", "", CommandCall(global_.on_global_friend_disable)),
        ), ),
        CommandA("群聊", "是否启用群聊，若禁用，机器人将不会再回应群组消息", (
            CommandA("启用", "", CommandCall(global_.on_global_group_enable)),
            CommandA("禁用", "", CommandCall(global_.on_global_group_disable)),
        ), ),
    ), ),
)


@dataclass
class CommandRequest:
    app: Ariadne
    source: CommandSource

    callable: Callable
    args: tuple = tuple()


def parse_command(app: Ariadne, source: CommandSource, command_path: tuple, command_a: CommandA,
                  text: str) -> CommandRequest:
    if type(command_a.content) == tuple:
        for command in command_a.content:
            subcommand_a: CommandA = command
            if not text.startswith(subcommand_a.command):
                continue
            return parse_command(app, source, command_path + (subcommand_a.command,), subcommand_a,
                                 text[len(command_a.command):].strip())
        else:
            if len(text) == 0:
                raise error.InvalidCommandError(command_a.help_text(command_path))

            raise error.InvalidCommandError(
                f"无效的命令: {text}" + "\n" + "\n" + command_a.help_text(command_path)
            )
    if type(command_a.content) == CommandCall:
        if command_a.content.default_arg is None:
            return CommandRequest(app, source, command_a.content.callable)
        else:
            if len(text) == 0:
                text = command_a.content.default_arg
            return CommandRequest(app, source, command_a.content.callable, (text,))


async def parse_do_command(app: Ariadne, source: CommandSource, text: str) -> bool:
    # 解析命令
    for command in root_command_map:
        command_a: CommandA = command
        if not text.startswith(command_a.command):
            continue
        req = parse_command(app, source, (command_a.command,), command_a,
                            text[len(command_a.command):].strip())
        break
    else:
        if len(text) == 0:
            raise error.InvalidCommandError(root_help_text())

        raise error.InvalidCommandError(
            f"无效的命令: {text}" + "\n" + "\n" + root_help_text()
        )

    # 验证是否有权限
    if source.from_id != app.account and source.from_id not in instance.config.accounts_map[app.account].masters:
        raise error.PermissionDeniedError(f"抱歉，您没有权限执行此操作")

    # 执行命令
    await req.callable(app, source, *req.args)

    return True


async def parse_do(app: Ariadne, source: CommandSource, text: str) -> bool:
    text = text.strip()
    if not text.startswith("、"):
        return False
    text = text[1:].strip()

    try:
        await parse_do_command(app, source, text)
    except error.AdminError as e:
        await source.reply(app, str(e))

    return True
